package cn.isjinhao.se.sql.evaluation.visitor;

import cn.isjinhao.se.sql.evaluation.EvaluationException;
import cn.isjinhao.se.sql.function.evaluator.FunctionEvaluator;
import cn.isjinhao.se.sql.function.evaluator.DefaultFunctionEvaluator;
import cn.isjinhao.se.sql.grammar.CurdStatement;
import cn.isjinhao.se.sql.grammar.CurdStatementVisitor;
import cn.isjinhao.se.sql.grammar.statement.*;
import cn.isjinhao.se.sql.lexical.token.Token;

import java.util.List;

/**
 * 对表达式进行计算。是一个delegate。供 SingleSelectTableSegVisitor 和 SingleSelectWhereSegVisitor使用。
 * <p>
 * <br/>
 * <p>
 * 能处理的数据类型包括：
 * char、Character、short、Short、byte、Byte、int、Integer、long、Long、double、Double、float、Float
 * boolean、Boolean、String、Date、LocalDate、LocalDateTime、BigDecimal、BigInteger、Enum。
 *
 * @Author ISJINHAO
 * @Date 2021/4/11 17:04
 */
public class StatementCalculator extends DelegateVisitor<Object> {

    private FunctionEvaluator functionEvaluator = DefaultFunctionEvaluator.getInstance();

    private EvaluationRunTimeContext evaluationRunTimeContext;

    private CurdStatementType type;

    public StatementCalculator(CurdStatementVisitor<Object> client, EvaluationRunTimeContext evaluationRunTimeContext, CurdStatementType type) {
        super(client);
        this.evaluationRunTimeContext = evaluationRunTimeContext;
        this.type = type;
    }

    @Override
    public Object visitWhereSeg(WhereSeg whereSeg) {
        return whereSeg.getLogic().accept(this);
    }

    @Override
    public Object visitLogic(Logic logic) {
        Object left = logic.getLeftSelectStatement().accept(this);
        CurdStatement rightSelectStatement = logic.getRightSelectStatement();
        if (rightSelectStatement != null) {
            Object right = rightSelectStatement.accept(this);
            Token token = logic.getToken();
            assertBoolean(left, "逻辑表达式需要操作布尔类型的数据");
            assertBoolean(right, "逻辑表达式需要操作布尔类型的数据");
            switch (token.getType()) {
                case AND:
                    return (Boolean) left && (Boolean) right;
                case OR:
                    return (Boolean) left || (Boolean) right;
                default:
                    throw new EvaluationException("操作符类型不对，需要 'and' 或 'or'，当前是：" + token.getLiteral());
            }
        }
        assertBoolean(left, "逻辑表达式需要操作布尔类型的数据");
        return left;
    }

    @Override
    public Object visitComparison(Comparison comparison) {
        Object left = comparison.getLeftSelectStatement().accept(this);
        CurdStatement rightSelectStatement = comparison.getRightSelectStatement();
        if (rightSelectStatement != null) {
            Object right = rightSelectStatement.accept(this);
            Token operator = comparison.getToken();
            switch (operator.getType()) {
                case BANG_EQUAL:
                    return !CalculatorDelegate.equal(left, right);
                case EQUAL:
                    return CalculatorDelegate.equal(left, right);
                case GREATER:
                    return CalculatorDelegate.greater(left, right);
                case GREATER_EQUAL:
                    return CalculatorDelegate.greaterEqual(left, right);
                case LESS:
                    return CalculatorDelegate.less(left, right);
                case LESS_EQUAL:
                    return CalculatorDelegate.lessEqual(left, right);
                case LIKE:
                    return CalculatorDelegate.like(left, right);
                case CONTAINS:
                    return CalculatorDelegate.contains(left, right);
                case IS:
                    return CalculatorDelegate.is(left, right);
                default:
                    throw new EvaluationException("操作符类型不对，需要 '>' 或 '<' 或 '>=' 或 '<=' 或 '=' 或 '!=' 或 'like' 或 'contains' 或 'is'，当前是：" + operator.getLiteral());
            }
        }
        assertBoolean(left, "比较表达式需要操作布尔类型的数据");
        return left;
    }

    private void assertBoolean(Object left, String msg) {
        if (!(left instanceof Boolean)) {
            throw new EvaluationException(msg);
        }
    }

    @Override
    public Object visitBinaryArithmetic(BinaryArithmetic binaryArithmetic) {
        Object left = binaryArithmetic.getLeftSelectStatement().accept(this);
        CurdStatement rightSelectStatement = binaryArithmetic.getRightSelectStatement();
        if (rightSelectStatement != null) {
            Object right = rightSelectStatement.accept(this);
            Token operator = binaryArithmetic.getToken();
            switch (operator.getType()) {
                case PLUS:
                    return CalculatorDelegate.plus(left, right);
                case MINUS:
                    return CalculatorDelegate.minus(left, right);
                case SLASH:
                    return CalculatorDelegate.divide(left, right);
                case STAR:
                    return CalculatorDelegate.multiply(left, right);
                default:
                    throw new EvaluationException("操作符类型不对，需要 '+' 或 '-' 或 '*' 或 '/'，当前是：" + operator.getLiteral());
            }
        }
        if (left instanceof Boolean) {
            return left;
        }
        throw new EvaluationException("比较表达式需要操作布尔类型的数据");
    }

    @Override
    public Object visitUnaryArithmetic(UnaryArithmetic unaryArithmetic) {
        Object accept = unaryArithmetic.getSelectStatement().accept(this);
        Token operator = unaryArithmetic.getOperator();
        switch (operator.getType()) {
            case BANG:
                return CalculatorDelegate.nand(accept);
            case MINUS:
                return CalculatorDelegate.negate(accept);
            default:
                throw new EvaluationException("操作符类型不对，需要 '-' 或 '!'，当前是：" + operator.getLiteral());
        }
    }

    @Override
    public Object visitLiteral(Literal literal) {
        Token token = literal.getValue();
        Object object = token.getLiteral();
        if (object instanceof String) {
            // 单独处理 true 和 false
            String string = (String) object;
            if ("true".equals(string)) {
                return true;
            } else if ("false".equals(string)) {
                return false;
            } else if ("null".equals(string)) {
                return null;
            }
        }
        return object;
    }

    @Override
    public Object visitGrouping(Grouping grouping) {
        return grouping.getSelectStatement().accept(this);
    }

    @Override
    public Object visitIdentifier(Identifier identifier) {
        return evaluationRunTimeContext.get((String) identifier.getName().getLiteral());
    }

    /**
     * 当存在函数的递归调用时，在调用者处完成递归。降低FunctionEvaluator的实现难度。
     *
     * @param function
     * @return
     */
    @Override
    public Object visitFunction(Function function) {
        List<CurdStatement> parameterList = function.getParameterList();
        int size = parameterList == null ? 0 : parameterList.size();
        Object[] parameters = new Object[size];
        for (int i = 0; i < size; i++) {
            CurdStatement item = parameterList.get(i);
            parameters[i] = item.accept(this);
        }
        return functionEvaluator.evaluate(function, type, parameters);
    }

    @Override
    public Object visitAssignmentList(AssignmentList assignmentList) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Object visitTimeInterval(TimeInterval timeInterval) {
        return timeInterval;
    }

    @Override
    public Object visitTimeUnit(TimeUnit timeUnit) {
        Object accept = timeUnit.getCurdStatement().accept(this);
        return new TimeUnit(timeUnit.getToken(), new Literal(new Token(null, accept)));
    }

    @Override
    public Object visitAttachment(Attachment attachment) {
        return attachment;
    }

}
